package ca.concordia.soen6011.tests;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

import ca.concordia.soen6011.Result;
import ca.concordia.soen6011.exceptions.InfinityOutputException;
import ca.concordia.soen6011.exceptions.UndefinedOutputException;
import ca.concordia.soen6011.impl.Cos;
import ca.concordia.soen6011.impl.LogBaseEofX;
import ca.concordia.soen6011.impl.Sin;
import ca.concordia.soen6011.impl.Tan;
import ca.concordia.soen6011.impl.TenPowerX;
import ca.concordia.soen6011.impl.XPowerX;
import ca.concordia.soen6011.interfaces.MathFunction;

/**
 * Tests everything using test data file
 * 
 * This class loads all the test data from the specified
 * test data file and then performs all the tests
 * 
 * @author Mirza, Musab Masood
 * @since July-August 2014
 * @copyright TRANSIENT FUNCTION CALCULATOR is a team project for the SOEN 6011
 *            course delivered by Dr. Kamthan, Pankaj at Concordia University,
 *            Montreal for the summer 2014 session.
 */


public class TestEverything {
	
	private boolean completedSetup = false;
	
	/**
	 * The mathfunctions to test agains
	 */
	private  MathFunction sin, cos, tan, logBaseEofX, tenPowerOfX, xPowerOfX;
	
	/**
	 * The conversion to TestDataItem for each item in the test data file
	 */
	private ArrayList<TestDataItem> testData;

	
	
	@Before
	public void setUp(){
		if (completedSetup)
			return;
		sin = new Sin();
		cos = new Cos();
		tan = new Tan();
		logBaseEofX = new LogBaseEofX();
		tenPowerOfX = new TenPowerX();
		xPowerOfX = new XPowerX();
		testData = new ArrayList<TestDataItem>();
		loadTestData();
		completedSetup = true;
	}

	/**
	 * Loads the test data into the Map
	 */
	private void loadTestData() {
		
		System.out.println("Loading test data from file...");
		BufferedReader br = null;
		try {
			br = new BufferedReader(new FileReader("testdata//testdata.csv"));
			String line;
			boolean headerSkipped = false;
			while ((line = br.readLine()) != null) {
				if (headerSkipped == false){
					headerSkipped = true;
					continue;
				} else if (!line.isEmpty()) {
					try {
						TestDataItem item = new TestDataItem(line);
						testData.add(new TestDataItem(line));
//						 System.out.println(item.toString());						
					} catch (Exception e) {
						System.out.println("---- Exception: " + e.getMessage());
					}
				}	
			}
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (br != null) {
				try {
					br.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		System.out.println("Test data loaded successfully with " + testData.size() + " test cases");
	}
	
	@Test
	public void testAllUndefinedOutputCases(){
		System.out.println("\nTesting undefined cases...");
		for (TestDataItem item : testData){
			if (item.isUndefinedOutput()){
				try {
					getMathFunction(item.getFunctionCode()).compute(item.getX(), item.getPrecision());
					fail("Didn't throw any exception");
				} catch (InfinityOutputException e) {
					fail("Threw infinity output exception");
				} catch (UndefinedOutputException e) {
					// pass
				}
			}
		}
	}
	
	
	@Test
	public void testsAllInfinityOutputCases(){
		System.out.println("\nTesting infinity cases...");
		for (TestDataItem item : testData){
			if (item.isInfiniteOutput()){
				try {
					getMathFunction(item.getFunctionCode()).compute(item.getX(), item.getPrecision());
					fail("Didn't throw any exception");
				} catch (InfinityOutputException e) {
					// pass
				} catch (UndefinedOutputException e) {
					fail("Threw undefined output exception");
				}
			}
		}
	}
	
	
	@Test
	public void testAllRegularCases(){
		System.out.println("\nTesting regular cases...");
		for (TestDataItem item : testData){
			if (!item.isInfiniteOutput() && !item.isUndefinedOutput()){
				try {
					System.out.println("Testing: " + item.getLine());
					Result r = getMathFunction(item.getFunctionCode()).compute(item.getX(), item.getPrecision());
					assertEquals(item.getExpectedOutput(), r.getOutput(), r.getPrecision());
				} catch (InfinityOutputException e) {
					fail("Threw infinity output exception");
				} catch (UndefinedOutputException e) {
					fail("Threw undefined output exception");
				}
			}
		}
	}
	

	/**
	 * Returns the math function based on the char code
	 * @param f
	 * @return
	 */
	private MathFunction getMathFunction(char f) {
		switch (f) {
		case 'a':
			return sin;
		case 'b':
			return cos;
		case 'c':
			return tan;
		case 'd':
			return tenPowerOfX;
		case 'e':
			return xPowerOfX;
		case 'f':
			return logBaseEofX;
		}
		return null;
	}
}
